//! Copyright 2012 Thongnh, Inc.
//! All rights reserved
//!
#include "StrLib.h"
#include "Rtc.h"

#if defined (__FW_BUILD__)
extern "C"  {
#include "stm32f10x.h"
#include "Counter.h"
    volatile U8 s_UpdateTimeFlag;
    void RTC_IRQHandler(void)
    {
        if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
        {
            /* Clear the RTC Second interrupt */
            RTC_ClearITPendingBit(RTC_IT_SEC);

            /* Enable time update */
            s_UpdateTimeFlag = 1;

            /* Wait until last write operation on RTC registers has finished */
            RTC_WaitForLastTask();
        }
    }
};
#else
    volatile U8 cRtc::s_UpdateTimeFlag;
#endif
    U32 cRtc::s_RtcCount;
    cRtc::st_StrTime cRtc::s_RtcStr;

    const U16 date_leap_const[13] =  {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
    const U16 date_const[13] =       {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};

void cRtc::Configuration()
{
#if defined (__FW_BUILD__)
    /* Enable PWR and BKP clocks */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    /* Allow access to BKP Domain */
    PWR_BackupAccessCmd(ENABLE);

    /* Reset Backup Domain */
    BKP_DeInit();

    // Disable Tamper
    BKP_TamperPinCmd(DISABLE);
    BKP_ITConfig(DISABLE);
    BKP_ClearITPendingBit();
    BKP_ClearFlag();

    // use PC13 as Second output
    //BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);

    /* Enable LSE */
    RCC_LSEConfig(RCC_LSE_ON);
    /* Wait till LSE is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
    {}

    /* Select LSE as RTC Clock Source */
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

    /* Enable RTC Clock */
    RCC_RTCCLKCmd(ENABLE);

    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();

    /* Enable the RTC Second */
    RTC_ITConfig(RTC_IT_SEC, ENABLE);

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();

    /* Set RTC prescaler: set RTC period to 1sec */
    RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
#endif
}

/**
  * @brief  Adjusts time.
  * @param  None
  * @retval None
  */
void cRtc::TimeAdjust(U32 iTimeCount)
{
#if defined (__FW_BUILD__)
    /* Enable PWR and BKP clocks */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    /* Allow access to BKP Domain */
    PWR_BackupAccessCmd(ENABLE);

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    /* Change the current time */
    RTC_SetCounter(iTimeCount);
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
#endif
}

void RTC_DisableTamper()
{   
#if defined (__FW_BUILD__)
    BKP_TamperPinCmd(DISABLE);
    BKP_TamperPinLevelConfig(BKP_TamperPinLevel_High);
    BKP_ITConfig(DISABLE);
    BKP_ClearFlag();
    BKP_ClearITPendingBit();
    BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);
#endif
}


void cRtc::InitReset()
{
#if defined (__FW_BUILD__)
    NVIC_InitTypeDef NVIC_InitStructure;

    RTC_DisableTamper();

    /* Configure one bit for preemption priority */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    /* Enable the RTC Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
    {
        /* RTC Configuration */
        Configuration();

        /* Adjust time by values entered by the user */
        s_RtcStr.m_year      = 14;
        s_RtcStr.m_month     = 11;
        s_RtcStr.m_date      = 11;
        s_RtcStr.m_hour      = 20;
        s_RtcStr.m_minute    = 30;
        s_RtcStr.m_date      = 00;
        TimeAdjust(StrToTime(&s_RtcStr));

        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
    }
    else
    {
        /* Wait for RTC registers synchronization */
        RTC_WaitForSynchro();

        /* Enable the RTC Second */
        RTC_ITConfig(RTC_IT_SEC, ENABLE);

        /* Wait until last write operation on RTC registers has finished */
        RTC_WaitForLastTask();
    }

    RCC_ClearFlag();
    s_RtcCount = RTC_GetCounter();
    s_RtcStr = IntToTime(s_RtcCount);
#endif


}


bool cRtc::IsLeapYear(U16 iYear)
{
    if((iYear%400)==0)
    {
        return (true);
    }
    else if((iYear%100)==0)
    {
        return (false);
    }
    else if((iYear%4)==0)
    {
        return (true);
    }
    else
    {
        return (false);
    }
}

/************************************************************************************
  * @brief Determines the weekday
  * @param Year,Month and Day
  * @retval :Returns the CurrentWeekDay Number 1- Sunday 7- Saturday
************************************************************************************/
U8 cRtc::DayOfWeek(U16 CurrentYear,U8 CurrentMonth,U8 CurrentDay)
{
    U16 Temp1,Temp2,Temp3,Temp4,CurrentWeekDay;

    if(CurrentMonth < 3)
    {
        CurrentMonth=CurrentMonth + 12;
        CurrentYear=CurrentYear-1;
    }

    Temp1=(6*(CurrentMonth + 1))/10;
    Temp2=CurrentYear/4;
    Temp3=CurrentYear/100;
    Temp4=CurrentYear/400;
    CurrentWeekDay = CurrentDay + (2 * CurrentMonth) + Temp1 + CurrentYear + Temp2 - Temp3 + Temp4 +1;
    CurrentWeekDay = CurrentWeekDay % 7;
    // CurrentWeekDay++;

    return((U8)CurrentWeekDay);
}


U32 cRtc::TotalDate(st_StrTime *strTime)
{
    U16 cur_year = strTime->m_year + 2000;
    U16 year = 2000;
    U32 total_date = 0;

    while (year < cur_year)
    {
        if (true == IsLeapYear(year))
        {
            total_date += 366;
        }
        else
        {
            total_date += 365;
        }
        year++;
    }
    if (true == IsLeapYear(cur_year))
    {
        total_date += date_leap_const[strTime->m_month];
    }   
    else
    {
        total_date += date_const[strTime->m_month];
    }
    return (total_date + strTime->m_date);
}


U32 cRtc::StrToTime(st_StrTime *strTime)
{
    return ( (TotalDate(strTime) * (3600*24)) + (strTime->m_hour * 3600) + (strTime->m_minute * 60) + strTime->m_second );
}


cRtc::st_StrTime cRtc::IntToTime(U32 time_cnt)
{
    st_StrTime strTime;
    U16 year = 2000;
    U32 total_date = time_cnt / (3600*24);
    U32 temp1 = 0;
    U32 temp2;
    bool year_type;

    while (1)
    {
        if (true == IsLeapYear(year))
        {
            if (total_date < (temp1+366)) break;
            temp1 += 366;
        }
        else
        {
            if (total_date < (temp1+365)) break;
            temp1 += 365;
        }
        year++;
    }
    strTime.m_year = year - 2000;
    temp1 = total_date - temp1;
    year_type = IsLeapYear(year);
    U8 i = 1;
    while(i < 12)
    {
        if (true == year_type)
        {
            if (temp1 < date_leap_const[i+1]) break;
        }
        else
        {
            if (temp1 < date_const[i+1]) break;
        }
        i++;
    }
    strTime.m_month = i;
    if (true == year_type)
    {
        strTime.m_date = temp1 - date_leap_const[i];
    }
    else
    {
        strTime.m_date = temp1 - date_const[i];
    }

    temp1 = time_cnt % (3600*24);
    temp2 = temp1 % 3600;
    strTime.m_hour = temp1 / 3600;
    strTime.m_minute = temp2 / 60;
    strTime.m_second = temp2 % 60;
    return (strTime);
}


/********************************************************************************************
  * @brief Updates the Date (This function is called when 1 Day has elapsed
  * @param None
  * @retval :None
********************************************************************************************/
void cRtc::IncDate(st_StrTime *time)
{
    if( time->m_month == 1 || time->m_month == 3 ||     \
        time->m_month == 5 || time->m_month == 7 ||     \
        time->m_month == 8 || time->m_month == 10 ||    \
        time->m_month == 12)
    { 
        if(time->m_date < 31)
        {
            time->m_date++;
        }
        else
        {
            if(time->m_month != 12)
            {
                time->m_month++;
                time->m_date = 1;
            }
            else
            {
                time->m_month = 1;
                time->m_date = 1;
                time->m_year++;
            }
        }
    }
    else if(time->m_month == 4 || time->m_month == 6 ||     \
            time->m_month == 9 || time->m_month == 11)
    {
        if(time->m_date < 30)
        {
            time->m_date++;
        }
        else
        {
            time->m_month++;
            time->m_date = 1;
        }
    }
    else if(time->m_month == 2)
    {
        if(time->m_date < 28)
        {
            time->m_date++;
        }
        else if(time->m_date == 28)
        {
            if(true == IsLeapYear(time->m_year + 2000))
            {
                time->m_date++;
            }
            else
            {
                time->m_month++;
                time->m_date = 1;
            }
        }
        else if(time->m_date == 29)
        {
            time->m_month++;
            time->m_date = 1;
        }
    }
}


void cRtc::UpdateTime()
{
    U32 temp1, temp2;
    
    temp1 = s_RtcCount % (3600*24);
    if (temp1 == 0)
    {
        s_RtcStr.m_hour = 0;
        s_RtcStr.m_minute = 0;
        s_RtcStr.m_second = 0;
        IncDate(&s_RtcStr);
    }
    else
    {
        temp2 = temp1 % 3600;
        s_RtcStr.m_hour = temp1 / 3600;
        s_RtcStr.m_minute = temp2 / 60;
        s_RtcStr.m_second = temp2 % 60;
    }
}


void cRtc::RunOne()
{
#if defined (__FW_BUILD__)
    if (0 == s_UpdateTimeFlag) return;
    s_UpdateTimeFlag = 0;

    s_RtcCount = RTC_GetCounter();
    UpdateTime();
    cCounter::SetNeedCheckTimePoint();
#endif
}


bool cRtc::SetTimeAsStr(U8* pStrTime)
{
    //TODO: them buoc kiem tra thong so dau vao
    cStrLib::Strcpy((U8*)&s_RtcStr, pStrTime, 6);
    s_RtcCount = StrToTime(&s_RtcStr);
    TimeAdjust(s_RtcCount);
    return (true);
}

